// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/win/hwnd_subclass.h"

#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/win/window_impl.h"

namespace ui {

namespace {

    class TestWindow : public gfx::WindowImpl {
    public:
        TestWindow()
            : saw_message(false)
        {
        }
        ~TestWindow() override { }

        bool saw_message;

    private:
        // Overridden from gfx::WindowImpl:
        BOOL ProcessWindowMessage(HWND window,
            UINT message,
            WPARAM w_param,
            LPARAM l_param,
            LRESULT& result,
            DWORD msg_map_id) override
        {
            if (message == WM_NCHITTEST)
                saw_message = true;

            return FALSE; // Results in DefWindowProc().
        }

        DISALLOW_COPY_AND_ASSIGN(TestWindow);
    };

    class TestMessageFilter : public HWNDMessageFilter {
    public:
        TestMessageFilter()
            : consume_messages(false)
            , saw_message(false)
        {
        }
        ~TestMessageFilter() override { }

        // Setting to true causes the filter subclass to stop messages from reaching
        // the subclassed window procedure.
        bool consume_messages;

        // True if the message filter saw the message.
        bool saw_message;

    private:
        // Overridden from HWNDMessageFilter:
        bool FilterMessage(HWND hwnd,
            UINT message,
            WPARAM w_param,
            LPARAM l_param,
            LRESULT* l_result) override
        {
            if (message == WM_NCHITTEST) {
                saw_message = true;
                return consume_messages;
            }
            return false;
        }

        DISALLOW_COPY_AND_ASSIGN(TestMessageFilter);
    };

} // namespace

TEST(HWNDSubclassTest, Filtering)
{
    TestWindow window;
    window.Init(NULL, gfx::Rect(0, 0, 100, 100));
    EXPECT_TRUE(window.hwnd() != NULL);

    {
        TestMessageFilter mf;
        HWNDSubclass::AddFilterToTarget(window.hwnd(), &mf);

        // We are not filtering, so both the filter and the window should receive
        // this message:
        ::SendMessage(window.hwnd(), WM_NCHITTEST, 0, 0);

        EXPECT_TRUE(mf.saw_message);
        EXPECT_TRUE(window.saw_message);

        mf.saw_message = false;
        window.saw_message = false;

        mf.consume_messages = true;

        // We are now filtering, so only the filter should see this message:
        ::SendMessage(window.hwnd(), WM_NCHITTEST, 0, 0);

        EXPECT_TRUE(mf.saw_message);
        EXPECT_FALSE(window.saw_message);
    }
}

TEST(HWNDSubclassTest, FilteringMultipleFilters)
{
    TestWindow window;
    window.Init(NULL, gfx::Rect(0, 0, 100, 100));
    EXPECT_TRUE(window.hwnd() != NULL);

    {
        TestMessageFilter mf1;
        TestMessageFilter mf2;
        HWNDSubclass::AddFilterToTarget(window.hwnd(), &mf1);
        HWNDSubclass::AddFilterToTarget(window.hwnd(), &mf2);

        // We are not filtering, so both the filter and the window should receive
        // this message:
        ::SendMessage(window.hwnd(), WM_NCHITTEST, 0, 0);

        EXPECT_TRUE(mf1.saw_message);
        EXPECT_TRUE(mf2.saw_message);
        EXPECT_TRUE(window.saw_message);

        mf1.saw_message = false;
        mf2.saw_message = false;
        window.saw_message = false;

        mf1.consume_messages = true;

        // We are now filtering, so only the filter |mf1| should see this message:
        ::SendMessage(window.hwnd(), WM_NCHITTEST, 0, 0);

        EXPECT_TRUE(mf1.saw_message);
        EXPECT_FALSE(mf2.saw_message);
        EXPECT_FALSE(window.saw_message);
    }
}

TEST(HWNDSubclassTest, RemoveFilter)
{
    TestWindow window;
    window.Init(NULL, gfx::Rect(0, 0, 100, 100));
    EXPECT_TRUE(window.hwnd() != NULL);

    {
        TestMessageFilter mf1;
        TestMessageFilter mf2;
        HWNDSubclass::AddFilterToTarget(window.hwnd(), &mf1);
        HWNDSubclass::AddFilterToTarget(window.hwnd(), &mf2);

        ::SendMessage(window.hwnd(), WM_NCHITTEST, 0, 0);
        EXPECT_TRUE(mf1.saw_message);
        EXPECT_TRUE(mf2.saw_message);
        EXPECT_TRUE(window.saw_message);

        mf1.saw_message = false;
        mf2.saw_message = false;
        window.saw_message = false;

        // Remove a filter and try sending message again.
        HWNDSubclass::RemoveFilterFromAllTargets(&mf1);
        ::SendMessage(window.hwnd(), WM_NCHITTEST, 0, 0);
        EXPECT_FALSE(mf1.saw_message);
        EXPECT_TRUE(mf2.saw_message);
        EXPECT_TRUE(window.saw_message);
    }
}

} // namespace ui
